# include "AudioCapture.h"
# include <mmdeviceapi.h>
# include <Functiondiscoverykeys_devpkey.h>
# include <AudioClient.h>
# include <wrl/client.h>

AudioCapture::AudioCapture()
{
}

const wchar_t* AudioCapture::getName() const
{
	return this->m_name;
}

int AudioCapture::getSampleRate() const
{
	return this->m_sample_rate;
}

int AudioCapture::startCapture()
{
	if (this->m_alive) {
		this->stopCapture();
	}
	//开始采集
	this->m_mutex = new std::shared_mutex();
	HRESULT hr = this->m_audio_client->Start();
	if (hr != S_OK) {
		return AUDIO_CAPTURE_START_ERROR;
	}
	this->m_alive = true;
	this->m_capture_thread = new std::thread(&AudioCapture::capture, this);
	return S_OK;
}

void AudioCapture::stopCapture()
{
	if (!this->m_alive) {
		return;
	}
	this->m_alive = false;
	this->m_capture_thread->join();
	delete this->m_capture_thread;
	delete this->m_mutex;
}

AudioCapture::~AudioCapture()
{
	this->stopCapture();
	if (this->m_capture_client != nullptr) {
		this->m_capture_client->Release();
		this->m_capture_client = nullptr;
	}
	if (this->m_audio_client != nullptr) {
		this->m_audio_client->Release();
		this->m_audio_client = nullptr;
	}
	if (this->m_device != nullptr) {
		this->m_device->Release();
		this->m_device = nullptr;
	}
	CloseHandle(this->m_audio_samples_ready_event);
	delete[] this->m_name;
}

int AudioCapture::enumCaptureToVector(std::vector<AudioCapture*>*& _audioCaptureVector)
{
	_audioCaptureVector = new std::vector<AudioCapture*>();
	IMMDeviceEnumerator* enumerator = nullptr;
	IMMDeviceCollection* device_collection = nullptr;
	AudioCapture* temp = nullptr;
	LPWSTR device_id = nullptr;
	HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
	CLSCTX_ALL, IID_PPV_ARGS(&enumerator));
	enumerator->EnumAudioEndpoints(eAll, eConsole | eMultimedia, &device_collection);
	// enumerator->EnumAudioEndpoints(eCapture, eConsole, &device_collection);
	UINT device_size = 0;
	device_collection->GetCount(&device_size);
	for (int i = 0; i < device_size; ++i) {
		Microsoft::WRL::ComPtr<IPropertyStore> property_store;
		PROPVARIANT name_var;
		temp = new AudioCapture();
		device_collection->Item(i, &temp->m_device);
		temp->m_device->GetId(&device_id);
		temp->m_device->OpenPropertyStore(STGM_READ, property_store.GetAddressOf());
		PropVariantInit(&name_var);
		property_store->GetValue(PKEY_Device_FriendlyName, &name_var);
		temp->setName(name_var.pwszVal, wcslen(name_var.pwszVal) + 1);
		CoTaskMemFree(device_id);
		PropVariantClear(&name_var);
		_audioCaptureVector->push_back(temp);
		temp->init();
	}
	return S_OK;
}

void AudioCapture::releaseCaputreFromVector(std::vector<AudioCapture*>*& _audioCaptureVector)
{
	if (_audioCaptureVector == nullptr) {
		return;
	}
	for (auto item : *_audioCaptureVector) {
		delete item;
	}
	delete _audioCaptureVector;
	_audioCaptureVector = nullptr;
}

void AudioCapture::setName(LPWSTR _name, size_t _size)
{
	this->m_name = new wchar_t[_size];
	wcscpy_s(this->m_name, _size, _name);
}

void AudioCapture::init()
{
	HRESULT hr = S_OK;
	hr = this->m_device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, reinterpret_cast<void**>(&this->m_audio_client));

	WAVEFORMATEX* mic_format = nullptr;
	// 获取音频引擎内如共享模式程序的音频流格式
	hr = this->m_audio_client->GetMixFormat(&mic_format);
	PWAVEFORMATEXTENSIBLE pEx = NULL;
	switch (mic_format->wFormatTag) {
	case WAVE_FORMAT_IEEE_FLOAT:
		break;
	case WAVE_FORMAT_EXTENSIBLE:
		pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(mic_format);
		if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
			pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
			pEx->Samples.wValidBitsPerSample = 16;

			mic_format->wBitsPerSample = 16;
			mic_format->nBlockAlign = mic_format->nChannels * mic_format->wBitsPerSample / 8;
			mic_format->nAvgBytesPerSec = mic_format->nBlockAlign * mic_format->nSamplesPerSec;
		}
		else {
			CoTaskMemFree(mic_format);
			return;
		}
		break;
	default:
		break;
	}
	this->m_sample_rate = mic_format->nSamplesPerSec;
	this->m_sample_size = (mic_format->wBitsPerSample / 8) * mic_format->nChannels;
	/*
	*	AUDCLNT_STREAMFLAGS_EVENTCALLBACK允许设置事件通知回调 SetEventHandle才会有效果
	*/
	hr = this->m_audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_LOOPBACK, BUFFER_TIME_100NS, 0, mic_format, NULL);
	this->m_audio_samples_ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);

	//设置事件通知对象
	hr = this->m_audio_client->SetEventHandle(this->m_audio_samples_ready_event);

	//生成采集服务
	hr = this->m_audio_client->GetService(__uuidof(IAudioCaptureClient), (void**)&this->m_capture_client);

}

void AudioCapture::capture()
{
	HRESULT hr;
	byte* data = nullptr;
	uint32_t sample_count = 0, packet_length = 0;
	DWORD flag;
	HANDLE wait_array[3];
	wait_array[0] = this->m_audio_samples_ready_event;
	uint64_t ts = 0;
	while (this->m_alive) {
		DWORD waitResult = WaitForMultipleObjects(1, wait_array, FALSE, 100);
		switch (waitResult)
		{
		case WAIT_OBJECT_0 + 0:
			hr = this->m_capture_client->GetBuffer(&data, &sample_count, &flag, NULL, &ts);
			if (flag & AUDCLNT_BUFFERFLAGS_SILENT)
			{
				// printf("AUDCLNT_BUFFERFLAGS_SILENT \n");
			}

			if (flag & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
			{
				//printf("%06d # AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY \n", nCnt);
			}

			if (flag & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR)
			{
				//printf("%06d # AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR \n", nCnt);
			}

			if (this->captureCallback != nullptr) {
				this->captureCallback(data, sample_count, this->m_sample_size);
			}

			hr = this->m_capture_client->ReleaseBuffer(sample_count);

			if (hr != S_OK) {
				// printf("release error");
			}
			hr = this->m_capture_client->GetNextPacketSize(&packet_length);
			if (hr != S_OK) {
				// printf("GetNextPacketSize error");
			}
			break;
		default:
			break;
		}
	}
}
